/*
 * NetFTSampleGUI.java
 *
 * Created on February 8, 2006, 3:32 PM
 */


package netftsample;
import com.atiia.automation.sensors.*;
import javax.swing.*;
import java.text.*;
import java.awt.*;
import java.net.*;
import java.io.*;
import java.util.Calendar;
import java.lang.Thread;

/**GUI for Net F/T sample application
 *
 * @author  Sam Skuce (ATI Industrial Automation)
 */
public class NetFTSampleGUI extends javax.swing.JFrame {
    
    private double[] m_daftMaxes = { 100, 100, 100, 100, 100, 100 }; /*maximum 
     rated force/torque readings*/
    private double[] m_daftCountsPerUnit = {1, 1, 1, 1, 1, 1}; /*counts per 
        *unit force or torque for each axis*/
    /** Visualizes the forces and torques. */
    
    private String m_strForceUnits; /*The units of force.*/
    private String m_strTorqueUnits; /*The units of torque.*/
    private String m_strCalSN;
    private String m_strCalIndex;
    private String m_strCfgName;
    private String m_strCfgIndex;
    
    private JLabel[] m_lblaFTLabel; /*the labels that display the force/torque 
     readings*/
    private String m_strSensorAddress; /*The network address of the sensor.*/
    private JProgressBar[] m_progaFTReadingBar; /*the progress bars */
    private static final int NUM_FT_AXES = 6;
    private static final Color POSITIVE_COLOR = Color.blue, 
            NEGATIVE_COLOR = Color.green;
    private DecimalFormat m_dfReading;
    private NetFTSensor m_netFT; /*the Net F/T controller*/
    private NetFTReaderThread m_NetFTReaderThread = null; /*reads 
     Net F/T in a loop*/    
    private DatagramSocket m_cNetFTDataSocket; /*socket used for high-speed
                                                *data collection*/    
    private boolean m_bDoingHighSpeedCollection = false; /*whether or not
    *we're currently doing high speed data collection*/    
    private java.io.PrintWriter m_cDataWriter; /*writes high-speed data to
    file*/
    /** The RDT sample rate of the sensor. */
    private int m_iRDTSampleRate = 0;
    
    
    /**Thread which communicates with Net F/T
     */
    private class NetFTReaderThread extends Thread{
        
        private NetFTSensor m_netFT; /*the Net F/T controller*/
        private NetFTSampleGUI m_guiParent; /*the gui that is using this
                                           *thread*/                                           
        
        private boolean m_bKeepGoing = true; /*controls when to end this
                                              *thread*/
        
        
        /**
         * Creates a new Net F/T reader thread
         * @param setNetFT The initialized NetFTSensor to communicate with.
         * @param setParent The NetFTSampleGUI that is using this thread to
         * communicate with the Net F/T.
         */
        public NetFTReaderThread( NetFTSensor setNetFT, 
                NetFTSampleGUI setParent ){
            m_netFT = setNetFT;
            m_guiParent = setParent;
        }   
        
        /**Sets a flag to stop after the current read is complete*/
        public void stopReading(){
            m_bKeepGoing = false;
            this.interrupt(); /*wake this thread up if it's in it's sleeping
                               *phase*/
        }
        
        
        
        /**Reads the Net F/T sensor in a loop*/
        public void run(){
            NetFTRDTPacket cRDTData; /*the latest f/t data from the Net F/T*/
            while ( m_bKeepGoing ){                
                 try{
                    /*synchronize this in case they press the stop button
                         *while we're in the middle of reading the data*/
                    synchronized ( m_netFT ){
                        if ( m_bDoingHighSpeedCollection ){
                            JOptionPane.showMessageDialog(null, "high speed");
                            /*read batch RDT packets*/ 
                            final int PACKETS_TO_READ = 1000;                       
                            NetFTRDTPacket[] caRDTPackets = new 
                                NetFTRDTPacket[PACKETS_TO_READ];                        
                            caRDTPackets = m_netFT.readHighSpeedData( 
                                    m_cNetFTDataSocket, PACKETS_TO_READ );  
                            int i; /*generic loop/array index*/
                            /*Precondition: caRDTPackets contains the list of
                             *packets that were read from the Net F/T. 
                            *m_cDataWriter is open to file to collect data to.
                            *Postcondition: m_cDataWriter has the new F/T data.
                            *i == PACKETS_TO_READ.
                            */
                            for ( i = 0; i < PACKETS_TO_READ; i++ ){                                 
                                m_cDataWriter.println( dataCollectionLine( caRDTPackets[i] ) );
                            }
                            cRDTData = caRDTPackets[PACKETS_TO_READ - 1];
                        }else{
                            cRDTData = m_netFT.readSingleFTRecord();
                        }                        
                    }
                    m_guiParent.displayFTData( cRDTData );
                }catch ( SocketException sexc ){
                    m_guiParent.displayError("Socket Exception: " + 
                            sexc.getMessage() );
                }catch ( IOException iexc ){
                    m_guiParent.displayError( "IO Exception: " +
                            iexc.getMessage() );                      
                }
                try{
                    if ( !m_bDoingHighSpeedCollection ) {
                        Thread.sleep(100);
                    }
                }catch ( java.lang.InterruptedException iexc ){
                    /*do nothing, just continue.  This exception should
                     only be thrown if they try to stop the thread while
                     it's sleeping*/
                }
                
            }
        }
    }
    
    /**Formats a data packet for collection to file.
     * @param nftrdtp   The data packet to record to file.
     * @return  A formatted string containing the F/T data and current time, 
     * which can be written to file.
     */
    private String dataCollectionLine(NetFTRDTPacket nftrdtp)
    {    
        
        String outputString =  /* The formatted output line. */                                   
                "0x" + Integer.toHexString(nftrdtp.getStatus()) + "," +                                   
                nftrdtp.getRDTSequence() + "," +
                nftrdtp.getFTSequence() + "," + 
                nftrdtp.getFx() + "," + 
                nftrdtp.getFy() + "," + 
                nftrdtp.getFz() + "," + 
                nftrdtp.getTx() + "," + 
                nftrdtp.getTy() + "," + 
                nftrdtp.getTz() + "," +
                Calendar.getInstance().getTime().toString();
        return outputString;
    }
    
    /** Creates new form NetFTSampleGUI */
    public NetFTSampleGUI() {
        initComponents();        
        m_dfReading= new DecimalFormat( "#.000");        
        m_lblaFTLabel = new JLabel[] { lblFx, lblFy, lblFz, lblTx, lblTy, 
                            lblTz };
        m_progaFTReadingBar = new JProgressBar[] { progFx, progFy, progFz, 
                            progTx, progTy, progTz };                        
        m_strSensorAddress = "146.232.146.253";
//                JOptionPane.showInputDialog( this, "What is " +
//                "the address of the sensor?", "Sensor Address", 
//                JOptionPane.QUESTION_MESSAGE);
        this.setTitle(m_strSensorAddress + " - ATINetFT Demo");
//        URL urlIconPath = getClass().getResource("ATIFTSADemo_1.jpg"); //ClassLoader.getSystemClassLoader().getSystemResource("ati1.ico");
        stopAndRestartSingleReadings();
        
    }
    
    /**
     * Stops the F/T reader thread, and sets m_NetFTReaderThread = 
     * null.
     */
    private void stopReaderThread()
    {
        if ( null != m_NetFTReaderThread ){
            m_NetFTReaderThread.stopReading();
            m_NetFTReaderThread = null;
        }
    }
    
    /**Stops (if necessary) the net f/t reader thread, and restarts it using
     *the user options. Also sets the scaling factors for the f/t display.
     */
    private void stopAndRestartSingleReadings(){
        
        if ( null != m_NetFTReaderThread ){
            m_NetFTReaderThread.stopReading();   
            m_NetFTReaderThread = null;
        }
        try{
            m_netFT = new NetFTSensor( InetAddress.getByName( 
                                m_strSensorAddress ) );
        } catch ( UnknownHostException uhex ){
            displayError( "Unknown Host Exception: " + 
                                uhex.getMessage() );            
            return;
        }       
        if ( ! readConfigurationInfo() ) {
            return;
        }
        lblForceUnits.setText( "Force Units: " + m_strForceUnits );
        lblTorqueUnits.setText( "Torque Units: " + m_strTorqueUnits );
        lblCalIndex.setText("Calibration Index: " + (Integer.parseInt(m_strCalIndex) + 1));
        lblConfigIndex.setText("Config Index: " + (Integer.parseInt(m_strCfgIndex) + 1));
        lblCalSN.setText("Calibration Serial#: " + m_strCalSN);
        lblConfigName.setText("Config Name: " + m_strCfgName);
        m_NetFTReaderThread = new NetFTReaderThread( m_netFT, this );
        m_NetFTReaderThread.start();
        
    }
    
    /**
     *Reads a page from the integrated web server.
     *@param strUrlSuffix   The page on the web server to read.
     *@return  The text of the web page.
     *@throws MalformedURLException If strUrlSuffix doesn't point to a valid
     *web page address.
     *@throws IOException If there is an error reading the web page text.
     */
    private String readWebPageText( String strUrlSuffix ) throws 
          MalformedURLException, IOException
    {
        /*Reads the HTML from the web server.*/
        BufferedReader cBufferedReader;
        /*The url of the configuration page.*/
        String strURL = "http://" + m_strSensorAddress + "/" +
                strUrlSuffix;
        cBufferedReader = new BufferedReader ( new InputStreamReader ( new
                URL(strURL).openConnection().getInputStream()));        
        /*The text of the page.*/
        String strPageText = "";
        /*The last line read from the web stream.*/
        String strCurLine;
        /*Precondition: cBufferedReader is at the beginning of the page.
         *Postcondition: cBufferedReader is finished, strPageText =
         *the text of the page, strCurLine = last line read from the 
         *page.
         */
         while ( null != ( strCurLine = cBufferedReader.readLine() ) ) {            
            strPageText += strCurLine;
         }     
        return strPageText;
    }
    
    private String readNetFTAPI(int index)
    {
        try{
        String strXML = readWebPageText("netftapi2.xml?index="+index);
        return strXML;
        }catch(Exception e)
        {
            return "";
        }
    }
    
        private String readNetFTCalAPI(int index)
    {
        try{
        String strXML = readWebPageText("netftcalapi.xml?index="+index);
        return strXML;
        }catch(Exception e)
        {
            return "";
        }
    }
    
    private int findActiveCFG(String xmlText)
    {
       String[] strret = xmlText.split("<setcfgsel>");
       String[] strret2 = strret[1].split("</setcfgsel>");
       int activeConfig = Integer.parseInt(strret2[0]);
       return activeConfig;       
    }
    
    /**
     *Reads information about the sensor's configuration from the integrated
     *web server.
     *@return  True if configuration was succesfully read, false otherwise.
     */
    private boolean readConfigurationInfo()
    { 
        try
        {
        String mDoc = readNetFTAPI(0);
        int activeConfig = findActiveCFG(mDoc);
        mDoc = readNetFTAPI(activeConfig);
        m_strCfgIndex = "" + activeConfig;
        String[] parseStep1 = mDoc.split("<cfgcalsel>");
        String[] parseStep2 = parseStep1[1].split("</cfgcalsel>");
        String mCal = readNetFTCalAPI(Integer.parseInt(parseStep2[0]));
        m_strCalIndex = parseStep2[0];
        parseStep1 = mCal.split("<calsn>");
        parseStep2 = parseStep1[1].split("</calsn>");
        m_strCalSN = parseStep2[0];
        mDoc = readNetFTAPI(activeConfig);
        parseStep1 = mDoc.split("<cfgnam>");
        parseStep2 = parseStep1[1].split("</cfgnam>");
        m_strCfgName = parseStep2[0];        
        parseStep1 = mDoc.split("<cfgcpf>");
        parseStep2 = parseStep1[1].split("</cfgcpf>");        
        setCountsPerForce(Double.parseDouble(parseStep2[0]));
        parseStep1 = mDoc.split("<cfgcpt>");
        parseStep2 = parseStep1[1].split("</cfgcpt>");       
        setCountsPerTorque(Double.parseDouble(parseStep2[0]));
        parseStep1 = mDoc.split("<comrdtrate>");
        parseStep2 = parseStep1[1].split("</comrdtrate>");  
        m_iRDTSampleRate = (Integer.parseInt(parseStep2[0]));
        parseStep1 = mDoc.split("<scfgfu>");
        parseStep2 = parseStep1[1].split("</scfgfu>"); 
        m_strForceUnits = parseStep2[0];
        parseStep1 = mDoc.split("<scfgtu>");
        parseStep2 = parseStep1[1].split("</scfgtu>"); 
        m_strTorqueUnits = parseStep2[0];
        parseStep1 = mDoc.split("<cfgmr>");
        parseStep2 = parseStep1[1].split("</cfgmr>");
        String[] asRatings = parseStep2[0].split(";");
          for ( int i = 0; i < asRatings.length; i++ )
          {
              m_daftMaxes[i] = Double.parseDouble(asRatings[i]);
              if ( 0 == m_daftMaxes[i])
              {
                   m_daftMaxes[i] = 32768; /* Default maximum rating. */
              }
           }
        }catch(Exception e)
        {
            return false;            
        }
        return true;
    } 
    
    /**
     *Sets the maximum ratings for each axis.
     *@param strConfigPageText  The HTML code of the configuration page on the
     *integrated web server.
     */
    
    private void setCountsPerForce( double counts )
    {
        double dCountsPerForce = counts;
        if ( 0 == dCountsPerForce ){
            dCountsPerForce = 1;
            displayError( "Read a counts per force value of 0, setting it to " +
                    "1 instead.");
        }
        int i;
        for ( i = 0; i < 3; i++ )
        {
            m_daftCountsPerUnit[i] = dCountsPerForce;
        }
    }
    
    private void setCountsPerTorque( double counts )
    {
        double dCountsPerTorque = counts;
        if ( 0 == dCountsPerTorque ) {
            dCountsPerTorque = 1;
            displayError( "Read a counts per torque value of 0, setting it " +
                    "to 1 instead." );
        }
        int i; /*generic loop/array index.*/
        /*Precondition: dCountsPerForce has the counts per force, 
         *dCountsPerTorque is the counts per torque.
         *Postcondition: m_daftCountsPerUnit has the counts per unit for each
         *axis, i == 3.
         */
        for ( i = 0; i < 3; i++ )
        {
            m_daftCountsPerUnit[i+3] = dCountsPerTorque;
        }
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        jLabel4 = new javax.swing.JLabel();
        jLabel5 = new javax.swing.JLabel();
        jLabel6 = new javax.swing.JLabel();
        lblFx = new javax.swing.JLabel();
        lblFy = new javax.swing.JLabel();
        lblFz = new javax.swing.JLabel();
        lblTx = new javax.swing.JLabel();
        lblTy = new javax.swing.JLabel();
        lblTz = new javax.swing.JLabel();
        progFx = new javax.swing.JProgressBar();
        progFy = new javax.swing.JProgressBar();
        progFz = new javax.swing.JProgressBar();
        progTx = new javax.swing.JProgressBar();
        progTy = new javax.swing.JProgressBar();
        progTz = new javax.swing.JProgressBar();
        jLabel9 = new javax.swing.JLabel();
        lblStatus = new javax.swing.JLabel();
        lblRDTSeq = new javax.swing.JLabel();
        jLabel8 = new javax.swing.JLabel();
        jLabel7 = new javax.swing.JLabel();
        lblFTSeq = new javax.swing.JLabel();
        jLabel10 = new javax.swing.JLabel();
        scrollErrors = new javax.swing.JScrollPane();
        lstErrors = new javax.swing.JList();
        lstErrors.setModel( new DefaultListModel() );
        btnCollect = new javax.swing.JButton();
        txtFileName = new javax.swing.JTextField();
        btnSelectFile = new javax.swing.JButton();
        lblForceUnits = new javax.swing.JLabel();
        lblTorqueUnits = new javax.swing.JLabel();
        jButton1 = new javax.swing.JButton();
        jLabel11 = new javax.swing.JLabel();
        lblConfigIndex = new javax.swing.JLabel();
        lblConfigName = new javax.swing.JLabel();
        lblCalIndex = new javax.swing.JLabel();
        lblCalSN = new javax.swing.JLabel();
        btnClear = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("ATI Industrial Automation Net F/T Sample");
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
        });
        getContentPane().setLayout(null);

        jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        jLabel1.setText("Fx");
        getContentPane().add(jLabel1);
        jLabel1.setBounds(10, 50, 40, 14);

        jLabel2.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        jLabel2.setText("Fy");
        getContentPane().add(jLabel2);
        jLabel2.setBounds(10, 80, 40, 14);

        jLabel3.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        jLabel3.setText("Fz");
        getContentPane().add(jLabel3);
        jLabel3.setBounds(10, 110, 40, 14);

        jLabel4.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        jLabel4.setText("Tx");
        getContentPane().add(jLabel4);
        jLabel4.setBounds(10, 140, 40, 14);

        jLabel5.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        jLabel5.setText("Ty");
        getContentPane().add(jLabel5);
        jLabel5.setBounds(10, 170, 40, 14);

        jLabel6.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        jLabel6.setText("Tz");
        getContentPane().add(jLabel6);
        jLabel6.setBounds(10, 200, 40, 14);

        lblFx.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblFx.setText("0");
        lblFx.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED));
        getContentPane().add(lblFx);
        lblFx.setBounds(60, 50, 100, 18);

        lblFy.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblFy.setText("0");
        lblFy.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED));
        getContentPane().add(lblFy);
        lblFy.setBounds(60, 80, 100, 18);

        lblFz.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblFz.setText("0");
        lblFz.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED));
        getContentPane().add(lblFz);
        lblFz.setBounds(60, 110, 100, 18);

        lblTx.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblTx.setText("0");
        lblTx.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED));
        getContentPane().add(lblTx);
        lblTx.setBounds(60, 140, 100, 18);

        lblTy.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblTy.setText("0");
        lblTy.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED));
        getContentPane().add(lblTy);
        lblTy.setBounds(60, 170, 100, 18);

        lblTz.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblTz.setText("0");
        lblTz.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED));
        getContentPane().add(lblTz);
        lblTz.setBounds(60, 200, 100, 18);
        getContentPane().add(progFx);
        progFx.setBounds(170, 50, 310, 19);
        getContentPane().add(progFy);
        progFy.setBounds(170, 80, 310, 19);
        getContentPane().add(progFz);
        progFz.setBounds(170, 110, 310, 19);
        getContentPane().add(progTx);
        progTx.setBounds(170, 140, 310, 19);
        getContentPane().add(progTy);
        progTy.setBounds(170, 170, 310, 19);
        getContentPane().add(progTz);
        progTz.setBounds(170, 200, 310, 19);

        jLabel9.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        jLabel9.setText("Status");
        getContentPane().add(jLabel9);
        jLabel9.setBounds(10, 20, 40, 14);

        lblStatus.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblStatus.setText("0");
        lblStatus.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED));
        getContentPane().add(lblStatus);
        lblStatus.setBounds(60, 20, 100, 18);

        lblRDTSeq.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblRDTSeq.setText("0");
        lblRDTSeq.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED));
        getContentPane().add(lblRDTSeq);
        lblRDTSeq.setBounds(225, 20, 100, 18);

        jLabel8.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        jLabel8.setText("RDTSeq");
        getContentPane().add(jLabel8);
        jLabel8.setBounds(165, 20, 50, 14);

        jLabel7.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        jLabel7.setText("FTSeq");
        getContentPane().add(jLabel7);
        jLabel7.setBounds(330, 20, 40, 14);

        lblFTSeq.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        lblFTSeq.setText("0");
        lblFTSeq.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED));
        getContentPane().add(lblFTSeq);
        lblFTSeq.setBounds(380, 20, 100, 18);

        jLabel10.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
        jLabel10.setText("Errors");
        getContentPane().add(jLabel10);
        jLabel10.setBounds(10, 460, 60, 14);

        scrollErrors.setOpaque(false);
        scrollErrors.setPreferredSize(new java.awt.Dimension(100, 100));

        lstErrors.setForeground(new java.awt.Color(255, 0, 0));
        scrollErrors.setViewportView(lstErrors);

        getContentPane().add(scrollErrors);
        scrollErrors.setBounds(10, 480, 500, 90);

        btnCollect.setText("Collect Streaming");
        btnCollect.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnCollectActionPerformed(evt);
            }
        });
        getContentPane().add(btnCollect);
        btnCollect.setBounds(10, 430, 180, 23);

        txtFileName.setText("<please select a file>");
        getContentPane().add(txtFileName);
        txtFileName.setBounds(10, 400, 240, 20);

        btnSelectFile.setText("...");
        btnSelectFile.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnSelectFileActionPerformed(evt);
            }
        });
        getContentPane().add(btnSelectFile);
        btnSelectFile.setBounds(260, 400, 30, 20);

        lblForceUnits.setText("Force units: ?");
        getContentPane().add(lblForceUnits);
        lblForceUnits.setBounds(10, 280, 180, 14);

        lblTorqueUnits.setText("Torque units: ?");
        getContentPane().add(lblTorqueUnits);
        lblTorqueUnits.setBounds(10, 300, 180, 14);

        jButton1.setText("Bias");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });
        getContentPane().add(jButton1);
        jButton1.setBounds(10, 240, 70, 23);

        jLabel11.setText("<HTML>Rotate cube by dragging mouse</HTML>");
        getContentPane().add(jLabel11);
        jLabel11.setBounds(190, 230, 100, 40);

        lblConfigIndex.setText("Config Index: ?");
        getContentPane().add(lblConfigIndex);
        lblConfigIndex.setBounds(10, 320, 180, 14);

        lblConfigName.setText("Config Name: ?");
        getContentPane().add(lblConfigName);
        lblConfigName.setBounds(10, 340, 290, 14);

        lblCalIndex.setText("Calibration Index: ?");
        getContentPane().add(lblCalIndex);
        lblCalIndex.setBounds(10, 360, 190, 14);

        lblCalSN.setText("Calibration Serial#: ?");
        getContentPane().add(lblCalSN);
        lblCalSN.setBounds(10, 380, 300, 14);

        btnClear.setText("Clear");
        btnClear.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnClearPerformed(evt);
            }
        });
        getContentPane().add(btnClear);
        btnClear.setBounds(393, 450, 110, 23);

        java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
        setBounds((screenSize.width-528)/2, (screenSize.height-632)/2, 528, 632);
    }// </editor-fold>//GEN-END:initComponents

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
        try{
            m_netFT.tare();
        }catch(SocketException sexc){
            displayError( "SocketException: " + sexc.getMessage());
        }catch(IOException ioexc){
            displayError("IOException: " + ioexc.getMessage());
        }
    }//GEN-LAST:event_jButton1ActionPerformed

    private void btnCollectActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCollectActionPerformed
        /*synchronize to avoid stopping the data collection in the middle
         *of trying to read data in the NetFTReaderThread*/
        synchronized ( m_netFT ){
            m_bDoingHighSpeedCollection = !m_bDoingHighSpeedCollection;
            /*if we were collecting data, stop it now*/
            if ( !m_bDoingHighSpeedCollection ){            
                btnCollect.setText( "Start Collecting" );
                try{
                    m_netFT.stopDataCollection( m_cNetFTDataSocket );
                    m_cDataWriter.close();
                }catch ( IOException cIOexc ){
                    displayError( "IOException: " + cIOexc.getMessage() );
                }
                /*go back to reading single data points in slow mode*/
                stopAndRestartSingleReadings();
                return;
            }      
            stopReaderThread();
        }
        
        try{
            m_cDataWriter = new PrintWriter( new FileWriter( 
                    txtFileName.getText() ) );
            Calendar curTime = Calendar.getInstance(); 
            String dateTimeString = DateFormat.getDateTimeInstance(
                    DateFormat.SHORT, DateFormat.SHORT ).format( 
                    curTime.getTime() );                    
            m_cDataWriter.println( "Start Time: " + dateTimeString );
            m_cDataWriter.println( "RDT Sample Rate: " + m_iRDTSampleRate);
            m_cDataWriter.println( lblForceUnits.getText() );
            m_cDataWriter.println( "Counts per Unit Force: " + 
                    m_daftCountsPerUnit[0] );
            m_cDataWriter.println( lblTorqueUnits.getText() );            
            m_cDataWriter.println( "Counts per Unit Torque: " +
                    m_daftCountsPerUnit[3] );
            m_cDataWriter.println( "Status (hex), RDTSequence, F/T Sequence, Fx, Fy, Fz," + 
                    " Tx, Ty, Tz, Time" );
        }catch ( IOException cIOExc ){
            /*if we can't open the file, don't do data collection*/
            displayError( "IOException: " + cIOExc.getMessage() );
            m_bDoingHighSpeedCollection = false;
            stopAndRestartSingleReadings();
            return;
        }
        
        /*we're starting a new round of high-speed data collection*/
        btnCollect.setText( "Stop Collecting" );
        
        /*set count to infinity - we'll stop collecting when the user
         *presses the "Stop Collecting" button*/        
        try{
            m_cNetFTDataSocket = m_netFT.startHighSpeedDataCollection( 
                    0 );
        }catch ( SocketException cSexc ){
            displayError( "SocketException: " + cSexc.getMessage() );
        }catch ( IOException cIOexc ){
            displayError( "IOException" + cIOexc.getMessage() );
        }
        m_NetFTReaderThread = new NetFTReaderThread( m_netFT, this );
        m_NetFTReaderThread.start();
    }//GEN-LAST:event_btnCollectActionPerformed

    private void btnSelectFileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSelectFileActionPerformed
        /*the file dialog used to choose the file to save data in.*/
        FileDialog cDataFileChooser = new FileDialog( this,
                "Choose File to Save Data to", FileDialog.SAVE );
        cDataFileChooser.setVisible( true );
        if ( null == cDataFileChooser.getFile() ){
            return;
        }
        txtFileName.setText( cDataFileChooser.getDirectory() + cDataFileChooser.getFile() );
        
        
    }//GEN-LAST:event_btnSelectFileActionPerformed

    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
        stopReaderThread();
        try {
            if ( m_bDoingHighSpeedCollection ){
                m_netFT.stopDataCollection( m_cNetFTDataSocket );
            }                    
        }catch ( IOException ioexc ){
            JOptionPane.showMessageDialog( this, "An IOException has " + 
                    "occurred: " + ioexc.getMessage(), "IOException", 
                    JOptionPane.ERROR_MESSAGE );
        }
        
    }//GEN-LAST:event_formWindowClosing

private void btnClearPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnClearPerformed
// TODO add your handling code here:
    lstErrors.setModel(new DefaultListModel());
}//GEN-LAST:event_btnClearPerformed
    
    /**Starts GUI
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new NetFTSampleGUI().setVisible(true);
            }
        });
    }
    
    /**Updates the error display with a specific error message.
     */
    private class UpdateErrorDisplay implements Runnable{
        private String m_sErrDesc; /*the error description*/
            
        /**displays the error text*/
        public void run(){
            Calendar curTime = Calendar.getInstance(); 
            String errString = DateFormat.getTimeInstance( 
                    DateFormat.MEDIUM ).format( curTime.getTime() ) + " - " +
                    m_sErrDesc;
            DefaultListModel errListModel = 
                    (DefaultListModel)lstErrors.getModel(); /*contains the list
                     *of errors*/
            errListModel.add( 0, errString ); 
            
        }
        
        /**Creates a new UpdateErrorDisplay.
         *@param setErrDesc     The String describing the error
         */
        public UpdateErrorDisplay( String setErrDesc ){
            m_sErrDesc = setErrDesc;
        }
    }
    
    /**Display an error in the last error field, including the time that
     *the error occurred.  Does the displaying using InvokeLater, so it's
     *thread safe.
     *@param errDesc    The String to display in the last error field.  The
     *time will be displayed in front of this text, so there is no need to put
     *the time in yourself.
     */
    public void displayError( String errDesc ){
        Runnable updateDisplay = new UpdateErrorDisplay( errDesc );
        javax.swing.SwingUtilities.invokeLater( updateDisplay );       
    }    
    
    
    
    
    /**Runnable which updates the Force and Torque display using data
     *from an RDT packet.  Used exclusively by the DisplayFTData method.
     */
    private class UpdateFTDisplay implements Runnable{
        
        private NetFTRDTPacket m_rdtPacket;
        
        /**Creates a new UpdateFTDisplay
         *@param setRDTPacket   The NetFTRDTPacket which contains the
         *F/T data to display.
         */
        public UpdateFTDisplay( NetFTRDTPacket setRDTPacket ){
            m_rdtPacket = setRDTPacket;
        }
        
        public void run(){
            lblStatus.setText( "0x" + String.format(  "%08x", 
                (int)m_rdtPacket.getStatus() ) );
            lblRDTSeq.setText( "" + m_rdtPacket.getRDTSequence() );
            lblFTSeq.setText( "" + m_rdtPacket.getFTSequence() );
            int ftCountsReading[] = m_rdtPacket.getFTArray();
            double ftRealReading[] = new double[NUM_FT_AXES];
            int i;
            for ( i = 0; i < NUM_FT_AXES; i++)
            {         
                /*display the numbers*/
                ftRealReading[i] = ftCountsReading[i] / m_daftCountsPerUnit[i];
                
                m_lblaFTLabel[i].setText( m_dfReading.format( ftRealReading[i] ) );            
                if ( ftRealReading[i] < 0)
                    m_progaFTReadingBar[i].setForeground( NEGATIVE_COLOR );
                else
                    m_progaFTReadingBar[i].setForeground( POSITIVE_COLOR );
                m_progaFTReadingBar[i].setValue( (int)Math.abs( ftRealReading[i] / 
                    m_daftMaxes[i] * 100 ) );
            } 
        }
    }
    
    /**Displays new F/T data using InvokeLater, so it's thread-safe
     *@param displayRDT - the NetFTRDTPacket containing the F/T data to
     *display
     */
    public void displayFTData( NetFTRDTPacket displayRDT ){
        UpdateFTDisplay updater = new UpdateFTDisplay( displayRDT );
        javax.swing.SwingUtilities.invokeLater( updater );
    }
    
    /**This method gets the latest force and torque values, and displays
     *them on the screen.
     */
    private void GetFTValues()
    {
        NetFTRDTPacket rdtData;
        try{
            rdtData = m_netFT.readSingleFTRecord();
        }catch ( SocketException sexc ){
            displayError( "SocketException: " + sexc.getMessage());
            return;
        }catch ( IOException iexc ){
            displayError( "IOException: " + iexc.getMessage());
            return;
        }
    }
    
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton btnClear;
    private javax.swing.JButton btnCollect;
    private javax.swing.JButton btnSelectFile;
    private javax.swing.JButton jButton1;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel10;
    private javax.swing.JLabel jLabel11;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JLabel jLabel8;
    private javax.swing.JLabel jLabel9;
    private javax.swing.JMenu jMenu1;
    private javax.swing.JMenuBar jMenuBar1;
    private javax.swing.JMenuItem jMenuItem1;
    private javax.swing.JLabel lblCalIndex;
    private javax.swing.JLabel lblCalSN;
    private javax.swing.JLabel lblConfigIndex;
    private javax.swing.JLabel lblConfigName;
    private javax.swing.JLabel lblFTSeq;
    private javax.swing.JLabel lblForceUnits;
    private javax.swing.JLabel lblFx;
    private javax.swing.JLabel lblFy;
    private javax.swing.JLabel lblFz;
    private javax.swing.JLabel lblRDTSeq;
    private javax.swing.JLabel lblStatus;
    private javax.swing.JLabel lblTorqueUnits;
    private javax.swing.JLabel lblTx;
    private javax.swing.JLabel lblTy;
    private javax.swing.JLabel lblTz;
    private javax.swing.JList lstErrors;
    private javax.swing.JProgressBar progFx;
    private javax.swing.JProgressBar progFy;
    private javax.swing.JProgressBar progFz;
    private javax.swing.JProgressBar progTx;
    private javax.swing.JProgressBar progTy;
    private javax.swing.JProgressBar progTz;
    private javax.swing.JScrollPane scrollErrors;
    private javax.swing.JTextField txtFileName;
    // End of variables declaration//GEN-END:variables
    
}
